package com.kpmg.datalake.common.dynamicdatasource;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;

/**
 * 切换数据源Advice
 * Order(-1) 保证该AOP在@Transactional之前执行
 *
 * @author Alex.zhao
 * @version v.0.1Id
 */
@Aspect
@Order(-1)
@Component
public class DynamicDataSourceAspect {
    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
    @Resource
    private DataSourceStoreService dataSourceStoreService;

    @Before("(@annotation(com.kpmg.datalake.common.dynamicdatasource.DynamicDS))&&(execution(* com.kpmg.datalake..*.*(..)))")
    public void beforeSwitchDS(JoinPoint point) {
        try {
            String dataSource = this.getDataSourceId(point);

            //如果不在我们注入的所有的数据源范围之内，那么输出警告信息，系统自动使用默认的数据源。
            if (!dataSourceStoreService.checkAndRefreshDataSource(dataSource)) {
                logger.error("datasource {} not exists,use default datasource", dataSource);
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Use datasource : {} > {}", dataSource, point.getSignature());
                }
                //找到的话，那么设置到动态数据源上下文中。
                DynamicDataSourceContextHolder.setDataSourceType(dataSource);
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

    /**
     * 获取datasourceId
     * param point 切点
     *
     * @return String
     */
    private String getDataSourceId(JoinPoint point) {

        final MethodSignature signature = (MethodSignature) point.getSignature();
        String dataSource = null;
        DynamicDS dynamicDS = signature.getMethod().getAnnotation(DynamicDS.class);
        //获取参数名
        String[] parameterNames = signature.getParameterNames();
        if (parameterNames != null) {
            for (int i = 0; i < parameterNames.length; i++) {
                if (Objects.equals(parameterNames[i], dynamicDS.value())) {
                    // 取出注解中的数据源名
                    dataSource = String.valueOf(point.getArgs()[i]);
                    break;
                }
            }
            if (dataSource == null) {
                dataSource = String.valueOf(point.getArgs()[0]);
            }
        } else if (point.getArgs().length > 0) {
            Object args = point.getArgs()[0];
            if (Map.class.isAssignableFrom(args.getClass())) {
                dataSource = String.valueOf(((Map<?, ?>) args).get(dynamicDS.value()));
            } else {
                dataSource = String.valueOf(args);
            }

        }
        //如果找不到对应参数 ，直接注解名称当作数据源ID
        if (dataSource == null) {
            dataSource = dynamicDS.value();
        }
        return dataSource;
    }

    @After("@annotation(com.kpmg.datalake.common.dynamicdatasource.DynamicDS)")
    public void afterSwitchDS(JoinPoint point) {
        if(logger.isDebugEnabled()){
            logger.debug("The method {} after switchDS ,DatasourceId {}" ,point.getSignature().getName(),DynamicDataSourceContextHolder.getDataSource());
        }
        DynamicDataSourceContextHolder.clearDataSource();

    }
    @AfterThrowing(value="@annotation(com.kpmg.datalake.common.dynamicdatasource.DynamicDS)",throwing="e")
    public void afterThrowing(JoinPoint point, Exception e){
        if(logger.isDebugEnabled()){
            logger.debug("The method {} occurs exception: {}" ,point.getSignature().getName(), e);
        }
        DynamicDataSourceContextHolder.clearDataSource();
    }

}
